package com.testlio.assignment.service;

import com.testlio.assignment.dto.CreateIssueRequest;
import com.testlio.assignment.dto.IssueDTO;
import com.testlio.assignment.dto.UpdateIssueRequest;
import com.testlio.assignment.mapper.IssueMapper;
import com.testlio.assignment.model.Issue;
import com.testlio.assignment.repository.IssueRepository;
import com.testlio.assignment.service.IssueRevisionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.security.core.context.SecurityContextHolder;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.HashMap;
import java.util.Map;

/**
 * Service class that handles business logic for Issue entities.
 */
@Service
@Transactional
public class IssueService {

    @Autowired
    private IssueRepository issueRepository;

    @Autowired
    private IssueMapper issueMapper;

    @Autowired
    private IssueRevisionService revisionService;

    /**
     * Retrieves all issues from the database.
     * @return List of all issues as DTOs
     */
    public List<IssueDTO> findAll() {
        return issueRepository.findAll().stream()
                .map(issueMapper::toDTO)
                .collect(Collectors.toList());
    }

    /**
     * Finds an issue by its ID.
     * @param id The ID of the issue to find
     * @return Optional containing the found issue DTO or empty if not found
     */
    public Optional<IssueDTO> findById(Integer id) {
        return issueRepository.findById(id)
                .map(issueMapper::toDTO);
    }

    private String getCurrentUserEmail() {
        return SecurityContextHolder.getContext().getAuthentication().getName();
    }

    /**
     * Creates a new issue.
     * @param request The create issue request
     * @return The created issue as DTO
     */
    public IssueDTO create(CreateIssueRequest request) {
        String userEmail = getCurrentUserEmail();
        Issue issue = issueMapper.toEntity(request);
        issue.setCreatedBy(userEmail);
        issue.setUpdatedBy(userEmail);
        Issue savedIssue = issueRepository.save(issue);

        // Create revision for new issue
        Map<String, String> changes = new HashMap<>();
        changes.put("title", issue.getTitle());
        changes.put("description", issue.getDescription());
        revisionService.createRevision(savedIssue, changes, userEmail);

        return issueMapper.toDTO(savedIssue);
    }

    /**
     * Updates an existing issue.
     * @param id The ID of the issue to update
     * @param request The update issue request
     * @return Optional containing the updated issue DTO or empty if not found
     */
    public Optional<IssueDTO> update(Integer id, UpdateIssueRequest request) {
        String userEmail = getCurrentUserEmail();
        return issueRepository.findById(id)
                .map(issue -> {
                    // Track changes
                    Map<String, String> changes = new HashMap<>();
                    if (!issue.getTitle().equals(request.getTitle())) {
                        changes.put("title", request.getTitle());
                    }
                    if (!issue.getDescription().equals(request.getDescription())) {
                        changes.put("description", request.getDescription());
                    }

                    // Update issue
                    issueMapper.updateEntityFromRequest(issue, request);
                    Issue updatedIssue = issueRepository.save(issue);

                    // Create revision if there are changes
                    if (!changes.isEmpty()) {
                        revisionService.createRevision(updatedIssue, changes, userEmail);
                    }

                    return issueMapper.toDTO(updatedIssue);
                });
    }

    /**
     * Deletes an issue by its ID.
     * @param id The ID of the issue to delete
     * @return true if the issue was deleted, false if it wasn't found
     */
    public boolean delete(Integer id) {
        return issueRepository.findById(id)
                .map(issue -> {
                    issueRepository.delete(issue);
                    return true;
                })
                .orElse(false);
    }
}
